package com.example.demo.excel.handler;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.IterUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.func.Func1;
import cn.hutool.poi.excel.sax.handler.RowHandler;
import com.example.demo.util.CustomExcelUtil;
import lombok.Getter;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * sax方式读取excel的bean处理器
 *
 * @author TongDaShuai
 * @since 2022/4/2
 */
@Getter
public abstract class CustomBeanHandler<T> implements RowHandler {

    /**
     * 标题所在行（从0开始计数）
     */
    private final int headerRowIndex;

    /**
     * 行数据转换函数
     */
    protected Func1<List<Object>, T> convertFunc;

    /**
     * 标题行列表
     */
    List<String> headerList;

    /**
     * 标题行对应的clazz字段列表
     */
    List<String> fieldList;

    /**
     *  数据行数量
     */
    private long dataRowNum = 0;

    /**
     * Bean类型
     */
    private Class<T> clazz;

    /**
     * 标题行别名。key对应着excel的标题，value对应着clazz对应的字段名。
     */
    private Map<String, String> headerAlias;

    /**
     * 每批次处理的数量
     */
    private int batchSize;

    /**
     * 每批次处理的数据
     */
    private List<T> batchDatas;


    /**
     * 每批次处理的默认数量
     */
    private static final int DEFAULT_BATCH_SIZE = 1000;

    /**
     * 构造
     *
     * @param headerRowIndex 标题所在行（从0开始计数）
     * @param clazz          Bean类型
     */
    public CustomBeanHandler(int headerRowIndex,  Class<T> clazz) {
        this(headerRowIndex, clazz, DEFAULT_BATCH_SIZE);
    }

    /**
     * 构造
     *
     * @param headerRowIndex 标题所在行（从0开始计数）
     * @param clazz          Bean类型
     * @param batchSize 每批次处理的数量
     */
    public CustomBeanHandler(int headerRowIndex,  Class<T> clazz, int batchSize) {
        // 获取标题头对应的class的列名的映射
        this.headerAlias =  CustomExcelUtil.getHeaderAlias(clazz, true);
        this.headerRowIndex = headerRowIndex;
        this.clazz = clazz;
        this.convertFunc = (rowList) -> BeanUtil.toBean(IterUtil.toMap(this.fieldList, rowList), this.clazz);
        this.batchSize = batchSize;
        this.batchDatas = new ArrayList<>(this.batchSize);
    }



    /**
     * 处理一行数据
     *
     * @param sheetIndex 当前Sheet序号
     * @param rowIndex   当前行号，从0开始计数
     * @param rowList    行数据列表
     */
    @Override
    public void handle(int sheetIndex, long rowIndex, List<Object> rowList) {
        // 忽略标题行前的数据
        if (rowIndex < headerRowIndex) {
            return;
        }

        // 标题行处理
        if (rowIndex == headerRowIndex) {
            // 标题行获取
            this.headerList = ListUtil.unmodifiable(Convert.toList(String.class, rowList));
            this.fieldList = headerList.stream().map(header -> headerAlias.get(header)).collect(Collectors.toList());
            return;
        }

        dataRowNum++ ;
        // 行数据转bean对象
        T t = this.convertFunc.callWithRuntimeException(rowList);
        this.batchDatas.add(t);

        // 最大批次数量处理
        if (this.batchDatas.size() >= this.batchSize) {
            handleBatch(this, this.batchDatas);
            this.batchDatas.clear();
        }


    }

    /**
     * 处理批次解析出来的数据
     */
    public abstract void handleBatch(CustomBeanHandler<T> customBeanHandler, List<T> batchDatas);



    /**
     * excel读取完后的处理
     */
    @Override
    public void doAfterAllAnalysed() {
        if (dataRowNum == 0) {
            throw new RuntimeException("读取的excel数据为空");
        }
        // 最后一个批次的数据处理
        handleBatch(this, this.batchDatas);
        this.batchDatas.clear();
    }
}
